package grpc

import (
	"context"
	"fmt"

	notificationv1 "github.com/devops-smartbot/devops-smartbot/proto/gen/go/notification/v1"
	"github.com/devops-smartbot/devops-smartbot/services/notification-service/internal/models"
	"github.com/devops-smartbot/devops-smartbot/services/notification-service/internal/service"
)

type Server struct {
	notificationv1.UnimplementedNotificationServiceServer
	notificationService *service.NotificationService
}

func NewServer(notificationService *service.NotificationService) *Server {
	return &Server{
		notificationService: notificationService,
	}
}

// SendNotification sends a notification
func (s *Server) SendNotification(ctx context.Context, req *notificationv1.SendNotificationRequest) (*notificationv1.SendNotificationResponse, error) {
	// Convert proto to internal model
	notification := &models.Notification{
		Channel:      channelProtoToModel(req.Channel),
		Recipients:   req.Recipients,
		Subject:      req.Subject,
		Body:         req.Body,
		Priority:     priorityProtoToModel(req.Priority),
		Source:       req.Source,
		SourceID:     req.SourceId,
		TemplateID:   req.TemplateId,
		TemplateData: req.TemplateData,
	}

	// Send notification
	id, err := s.notificationService.SendNotification(ctx, notification)
	if err != nil {
		return &notificationv1.SendNotificationResponse{
			NotificationId: "",
			Status:         notificationv1.Status_STATUS_FAILED,
			Message:        fmt.Sprintf("Failed to send notification: %v", err),
		}, nil
	}

	return &notificationv1.SendNotificationResponse{
		NotificationId: id,
		Status:         notificationv1.Status_STATUS_PENDING,
		Message:        "Notification queued successfully",
	}, nil
}

// GetNotificationHistory retrieves notification history
func (s *Server) GetNotificationHistory(ctx context.Context, req *notificationv1.GetNotificationHistoryRequest) (*notificationv1.GetNotificationHistoryResponse, error) {
	// Build filters
	filters := make(map[string]interface{})
	if req.Source != "" {
		filters["source"] = req.Source
	}
	if req.SourceId != "" {
		filters["sourceId"] = req.SourceId
	}
	if req.Channel != notificationv1.Channel_CHANNEL_UNSPECIFIED {
		filters["channel"] = channelProtoToModel(req.Channel)
	}
	if req.Status != notificationv1.Status_STATUS_UNSPECIFIED {
		filters["status"] = statusProtoToModel(req.Status)
	}

	// Get notifications
	page := int(req.Page)
	if page < 1 {
		page = 1
	}
	pageSize := int(req.PageSize)
	if pageSize < 1 {
		pageSize = 20
	}

	notifications, total, err := s.notificationService.GetNotificationHistory(ctx, filters, page, pageSize)
	if err != nil {
		return nil, err
	}

	// Convert to proto
	records := make([]*notificationv1.NotificationRecord, len(notifications))
	for i, n := range notifications {
		records[i] = notificationModelToProto(n)
	}

	return &notificationv1.GetNotificationHistoryResponse{
		Records: records,
		Total:   int32(total),
	}, nil
}

// UpdateUserPreferences updates user notification preferences
func (s *Server) UpdateUserPreferences(ctx context.Context, req *notificationv1.UpdateUserPreferencesRequest) (*notificationv1.UpdateUserPreferencesResponse, error) {
	prefs := &models.UserPreferences{
		UserID:             req.UserId,
		EmailEnabled:       req.EmailEnabled,
		SlackEnabled:       req.SlackEnabled,
		DingTalkEnabled:    req.DingtalkEnabled,
		SilentHours:        req.SilentHours,
		EventSubscriptions: req.EventSubscriptions,
	}

	err := s.notificationService.UpdateUserPreferences(ctx, prefs)
	if err != nil {
		return &notificationv1.UpdateUserPreferencesResponse{
			Success: false,
			Message: fmt.Sprintf("Failed to update preferences: %v", err),
		}, nil
	}

	return &notificationv1.UpdateUserPreferencesResponse{
		Success: true,
		Message: "Preferences updated successfully",
	}, nil
}

// Helper functions to convert between proto and internal models

func channelProtoToModel(channel notificationv1.Channel) models.Channel {
	switch channel {
	case notificationv1.Channel_CHANNEL_EMAIL:
		return models.ChannelEmail
	case notificationv1.Channel_CHANNEL_SLACK:
		return models.ChannelSlack
	case notificationv1.Channel_CHANNEL_DINGTALK:
		return models.ChannelDingTalk
	case notificationv1.Channel_CHANNEL_WEBHOOK:
		return models.ChannelWebhook
	default:
		return models.ChannelEmail
	}
}

func channelModelToProto(channel models.Channel) notificationv1.Channel {
	switch channel {
	case models.ChannelEmail:
		return notificationv1.Channel_CHANNEL_EMAIL
	case models.ChannelSlack:
		return notificationv1.Channel_CHANNEL_SLACK
	case models.ChannelDingTalk:
		return notificationv1.Channel_CHANNEL_DINGTALK
	case models.ChannelWebhook:
		return notificationv1.Channel_CHANNEL_WEBHOOK
	default:
		return notificationv1.Channel_CHANNEL_UNSPECIFIED
	}
}

func priorityProtoToModel(priority notificationv1.Priority) models.Priority {
	switch priority {
	case notificationv1.Priority_PRIORITY_LOW:
		return models.PriorityLow
	case notificationv1.Priority_PRIORITY_MEDIUM:
		return models.PriorityMedium
	case notificationv1.Priority_PRIORITY_HIGH:
		return models.PriorityHigh
	case notificationv1.Priority_PRIORITY_CRITICAL:
		return models.PriorityCritical
	default:
		return models.PriorityMedium
	}
}

func priorityModelToProto(priority models.Priority) notificationv1.Priority {
	switch priority {
	case models.PriorityLow:
		return notificationv1.Priority_PRIORITY_LOW
	case models.PriorityMedium:
		return notificationv1.Priority_PRIORITY_MEDIUM
	case models.PriorityHigh:
		return notificationv1.Priority_PRIORITY_HIGH
	case models.PriorityCritical:
		return notificationv1.Priority_PRIORITY_CRITICAL
	default:
		return notificationv1.Priority_PRIORITY_UNSPECIFIED
	}
}

func statusProtoToModel(status notificationv1.Status) models.Status {
	switch status {
	case notificationv1.Status_STATUS_PENDING:
		return models.StatusPending
	case notificationv1.Status_STATUS_SENT:
		return models.StatusSent
	case notificationv1.Status_STATUS_FAILED:
		return models.StatusFailed
	case notificationv1.Status_STATUS_RETRYING:
		return models.StatusRetrying
	default:
		return models.StatusPending
	}
}

func statusModelToProto(status models.Status) notificationv1.Status {
	switch status {
	case models.StatusPending:
		return notificationv1.Status_STATUS_PENDING
	case models.StatusSent:
		return notificationv1.Status_STATUS_SENT
	case models.StatusFailed:
		return notificationv1.Status_STATUS_FAILED
	case models.StatusRetrying:
		return notificationv1.Status_STATUS_RETRYING
	default:
		return notificationv1.Status_STATUS_UNSPECIFIED
	}
}

func notificationModelToProto(n *models.Notification) *notificationv1.NotificationRecord {
	record := &notificationv1.NotificationRecord{
		Id:           n.ID,
		Channel:      channelModelToProto(n.Channel),
		Recipients:   n.Recipients,
		Subject:      n.Subject,
		Body:         n.Body,
		Priority:     priorityModelToProto(n.Priority),
		Status:       statusModelToProto(n.Status),
		Source:       n.Source,
		SourceId:     n.SourceID,
		CreatedAtUnix: n.CreatedAt.Unix(),
		ErrorMessage: n.ErrorMessage,
		RetryCount:   int32(n.RetryCount),
	}

	if n.SentAt != nil {
		record.SentAtUnix = n.SentAt.Unix()
	}

	return record
}
